﻿/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is tyzoh.jp code.
 *
 * The Initial Developer of the Original Code is Kazutaka Kurihara.
 * Portions created by the Initial Developer are Copyright (C) 2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */
using System;
using System.Drawing;
using System.Collections;
using System.Runtime.Serialization.Formatters.Soap;
using System.IO;
using System.Windows.Forms;

namespace Qurihara.QBasic
{
	public class DrawingUtil
    {
        #region Image Serialize&Deserialize using Base64 with MD5 hash
        public static string BitmapBase64Serialize(Bitmap bm)
        {
            MemoryStream stream = new MemoryStream();
            bm.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            long pos = stream.Position;
            //long leng = stream.Length;
            stream.Close();
            byte[] b = stream.GetBuffer();
            string s = Convert.ToBase64String(b,0,(int)pos, Base64FormattingOptions.InsertLineBreaks);
            return s;
        }
        public static Bitmap BitmapBase64Deserialize(string base64string)
        {
            byte [] b = 
                Convert.FromBase64String(base64string);
            MemoryStream stream = new MemoryStream(b);
            Bitmap bm = new Bitmap(stream);
            stream.Close();
            return bm;
        }
        public static string GetHash(Bitmap bm)
        {
            MemoryStream stream = new MemoryStream();
            bm.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            long pos = stream.Position;
            //long leng = stream.Length;
            stream.Close();
            byte[] data = stream.GetBuffer();

            System.Security.Cryptography.MD5CryptoServiceProvider md5 =
                new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] bs = md5.ComputeHash(data,0,(int)pos);

            System.Text.StringBuilder result = new System.Text.StringBuilder();
            foreach (byte b in bs)
            {
                result.Append(b.ToString("x2"));
            }
            return result.ToString();
        }

        #endregion

        #region Image Serialize&Deserialize by SOAP
        public static string SOAPSerialize(Bitmap bm)
        {
            SoapFormatter soapFormatter = new SoapFormatter();
            MemoryStream stream = new MemoryStream();
            soapFormatter.Serialize(stream, bm);
            stream.Close();
            byte[] b = stream.GetBuffer();
            MemoryStream newStream = new MemoryStream(b);

            StreamReader sr = new StreamReader(newStream);
            char[] nul = { '\0' };
            string s = sr.ReadToEnd().TrimEnd(nul);
            sr.Close();
            newStream.Close();

            return System.Web.HttpUtility.HtmlEncode(s);
        }
        public static Bitmap SOAPDeserialize(string soapstring)
        {
            Bitmap bm;

            MemoryStream stream = new MemoryStream();
            StreamWriter sw = new StreamWriter(stream);
            System.Web.HttpUtility.HtmlDecode(soapstring, sw);
            sw.Close();
            stream.Close();

            byte[] b = stream.GetBuffer();
            MemoryStream newStream = new MemoryStream(b);
            SoapFormatter soapFormatter = new SoapFormatter();
            bm = (Bitmap)soapFormatter.Deserialize(newStream);
            newStream.Close();

            return bm;
        }

        #endregion

        /// <summary>
        /// ファイルメニューの名前からMenuItemを検索する
        /// </summary>
        public static MenuItem FindByName(Menu.MenuItemCollection src, string target)
        {
            foreach (MenuItem mit in src)
            {
                if (target.Equals(mit.Text)) return mit;
            }
            return null;
        }


		/// <summary>
		/// 四角形を1.5倍とか0.8倍とかにリサイズする．Inflateがあまりに使えないために作った．
		/// </summary>
		/// <param name="r">ソース四角形．出力でもある</param>
		/// <param name="ratio">倍率．</param>
		public static void ResizeByRatio(ref Rectangle r,float ratio)
		{
			r.Inflate((int)(r.Width*ratio)-r.Width,(int)(r.Height*ratio)-r.Height);
		}

		public static void ForceBoxInBounds(Rectangle myRect,Rectangle parentRect,ref Point adjustment)
		{//四角形が親四角形の中に収まるような補正量を求める
			adjustment.X=0;
			adjustment.Y=0;
			if (myRect.X < parentRect.X)
			{
				adjustment.X = parentRect.X - myRect.X;
			}
			if (parentRect.Right < myRect.Right)
			{
				adjustment.X = parentRect.Right - myRect.Right;
			}
			if (myRect.Y < parentRect.Y)
			{
				adjustment.Y = parentRect.Y - myRect.Y;
			}
			if (parentRect.Bottom < myRect.Bottom)
			{
				adjustment.Y = parentRect.Bottom - myRect.Bottom;
			}
		}
		
		public static Rectangle GetRectangleInAspectOf(float aspect,Rectangle src)
		{// Width/Heightのアスペクト比になるようなsrcを包む四角を返す。
			Rectangle rw = new Rectangle(src.Location,src.Size);
			rw.Width = (int)(rw.Height * aspect);
			SynchronizeRectangleCenter(ref rw,src);
			rw.Inflate(20,20);
			if(rw.Contains(src)) return rw;

			Rectangle rh = new Rectangle(src.Location,src.Size);
			rh.Height = (int)(rh.Width / aspect);
			SynchronizeRectangleCenter(ref rh,src);
			rh.Inflate(20,20);
			if(rh.Contains(src)) return rh;

			System.Diagnostics.Debug.Fail("unexpected to come here.");
			return new Rectangle(0,0,0,0);
		}
        public static Rectangle GetBoxedRectangleWithAspectRatioPreserved(Size boxedSize, Rectangle boxingRect)
        {// 上記と逆。boxedSizeのアスペクト比を保ちつつ、boxingRectに内接する四角を返す
            Rectangle rout = new Rectangle(0, 0, boxedSize.Width, boxedSize.Height);
            float ratio = (float)boxedSize.Width / (float)boxedSize.Height;

            // widthを親と同じにする場合
            rout.Width = boxingRect.Width;
            rout.Height = (int)((float)rout.Width / ratio);
            SynchronizeRectangleCenter(ref rout, boxingRect);
            if (boxingRect.Contains(rout)) return rout;

            // heightを親と同じにする場合
            rout.Height = boxingRect.Height;
            rout.Width = (int)((float)rout.Height * ratio);
            SynchronizeRectangleCenter(ref rout, boxingRect);
            if (boxingRect.Contains(rout)) return rout;

            System.Diagnostics.Debug.Fail("unexpected to come here.");
            return new Rectangle(0, 0, 0, 0);
        }

		public static void SynchronizeRectangleCenter(ref Rectangle src,Rectangle refe)
		{ // srcの中心をrefeと一致させる。
			Point p1 = new Point(src.X + src.Width/2,src.Y + src.Height/2);
			Point p2 = new Point(refe.X + refe.Width/2,refe.Y + refe.Height/2);
			src.X += p2.X - p1.X;
			src.Y += p2.Y - p1.Y;
		}
		public static PointF GetRelativePoint(Rectangle parent,Point src)
		{//parentを基準とした相対座標に変換する。
			src.X = src.X - parent.X;
			src.Y = src.Y - parent.Y;
			return new PointF((float)src.X/(float)parent.Width,(float)src.Y/(float)parent.Height);
		}
		public static RectangleF GetRelativeRectangle(Rectangle parent,Rectangle src)
		{
			src.X = src.X - parent.X;
			src.Y = src.Y - parent.Y;
			return new RectangleF((float)src.X/(float)parent.Width,(float)src.Y/(float)parent.Height
				,(float)src.Width/(float)parent.Width,(float)src.Height/(float)parent.Height);
		}

		///HSB空間から色を指定する．hue:0 to 360, saturation: 0 to 1, brightness: 0 to 1
		public static Color ColorFromHSB(float hue,float saturation,float brightness)
		{
			int r=0,g=0,b=0;
  	 	int h;
   		float f;
   		int p,q,t;
	
	   	if(hue>360.0f) hue-=360.0f;
   		else if(hue<0.0f) hue+=360.0f;
  	 	if(saturation>1.0f) saturation=1.0f;
   		else if(saturation<0.0f) saturation=0.0f;
   		int br=(int)(255*brightness);
   		if(br>255) br=255;
   		else if(br<0) br=0;
			if(saturation==0.0)
			{
    	  r=br;
      	g=br;
	      b=br;
  		}
			else
			{
      	h=(int)(hue/60.0f);
      	f=hue/60.0f-h;
      	p=(int)(br*(1.0f-saturation));
      	if(p<0) p=0;
      	if(p>255) p=255;
      	q=(int)(br*(1.0f-f*saturation));
      	if(q<0) q=0;
      	if(q>255) q=255;
      	t=(int)(br*(1.0f-(1.0f-f)*saturation));
      	if(t<0) t=0;
      	if(t>255) t=255;
	      switch(h)
				{
        	case 0:r=br; g=t; b=p; break;
					case 1:r=q; g=br; b=p; break;
					case 2:r=p; g=br; b=t; break;
					case 3:r=p; g=q; b=br; break;
					case 4:r=t; g=p; b=br; break;
					case 5:r=br; g=p; b=q; break;
				};
			}
			return Color.FromArgb(r,g,b);
		}
	}

	// ********************************
	public class Grid2DArrayList
	{
		//public static int MaxXResolution = 10;
		//public static int MaxYResolution = 10;
		protected QArrayList [] lists;
		public Grid2DArrayList(int x,int y)
		{
			//if (x > MaxXResolution) x = MaxXResolution;
			//if (y > MaxYResolution) y = MaxYResolution;	
			xReso = x;
			yReso = y;
			lists = new QArrayList[xReso*yReso];
			for (int i=0;i<lists.Length;i++)
			{
				lists[i] = new QArrayList();
			}
/*
			ArrayList [][] test;
			test[0] = new Array[2];
			test[1] = new Array[2];
*/
		}

		protected int xReso,yReso;
		public int XResolution
		{
			get{return xReso;}
		}
		public int YResolution
		{
			get{return yReso;}
		}
		public void Clear()
		{
			foreach(QArrayList a in lists)
			{
				a.Clear();
			}
		}
		/*
		// 点はいれても意味がないか。
		public void Add(float x,float y,object o)
		{
			lists[GetIndex(x,y)].Add(o);
		}
		*/
		public void Add(RectangleF r,object o)
		{// 領域の追加
			PointF topleft = r.Location;
			PointF bottomright = new PointF(r.Right,r.Bottom);
			for(int y = (int)(topleft.Y * yReso);y <= (int)(bottomright.Y * yReso);y++)
			{
				for(int x = (int)(topleft.X * xReso);x <= (int)(bottomright.X * xReso);x++)
				{
					if (0 <= x && x < xReso && 0 <= y && y < yReso)
						lists[GetIndex(x,y)].Add(o);
				}
			}
		}
/*
		//大変なので使わないほうが良い。
		public void Remove(object o)
		{
		}
*/
		protected int GetIndex(float x,float y)
		{
			return (int)(xReso*x) +xReso*(int)(yReso*y);
		}
		protected int GetIndex(int x,int y)
		{
			//return x * xReso + y;
			return xReso * y + x;
		}
		public void GetPointedGridList(float x,float y,ref QArrayList output)
		{
			output.Clear();
			if (0 <= x && x < 1 && 0 <= y && y < 1)
				output.Set(lists[GetIndex(x,y)]);
		}
	}

}